﻿//#include "stdafx.h"
#include "uHttpWebSocket.h"

//#include <iostream>
#include <sstream>
#include <QtCore/QList>
#include <QtNetwork/QtNetwork>
//#include <QtCore/QtCore>

/////////////////////////////////////////////////////////////////////////////////////////
namespace uWS {

	uHttpWebSocket::uHttpWebSocket()
		//: mInterrupted(false)
	{
	}


	uHttpWebSocket::~uHttpWebSocket()
	{
		UnInit();
	}

	uHttpWebSocket & uHttpWebSocket::Instance()
	{
		static uHttpWebSocket instance;
		return instance;
	}

	bool uHttpWebSocket::Init(int iPort, std::string strWebRoot, std::string strDefaultTarget)
	{
		UnInit();
		//
		//mInterrupted = false;

		// 参数保存，字符串必须保存，由于传入只保存指针，要确保字符串指针一直有效
		miPort = iPort;
		mstrWebRoot = strWebRoot;
		mstrDefaultTarget = strDefaultTarget;

		// 检查端口占用  netstat -aon|findstr "3000"
		bool bListenRes = false;
		for (;miPort < iPort + 50 && miPort < 65535 && !bListenRes; ++miPort)
		{
			addrinfo hints, *result;
			memset(&hints, 0, sizeof(addrinfo));
			hints.ai_flags = AI_PASSIVE;
			hints.ai_family = AF_UNSPEC;
			hints.ai_socktype = SOCK_STREAM;
			if (getaddrinfo(NULL, std::to_string(miPort).c_str(), &hints, &result)) {
				continue;
			}
			uS::Context ct;
			uv_os_sock_t listenFd = SOCKET_ERROR;
			addrinfo *listenAddr = NULL;
			for (addrinfo *a = result; a && listenFd == SOCKET_ERROR; a = a->ai_next)
			{
				if (a->ai_family == AF_INET) {
					listenFd = ct.createSocket(a->ai_family, a->ai_socktype, a->ai_protocol);
					listenAddr = a;
				}
			}
			for (addrinfo *a = result; a && listenFd == SOCKET_ERROR; a = a->ai_next) 
			{
				if (a->ai_family == AF_INET6) {
					listenFd = ct.createSocket(a->ai_family, a->ai_socktype, a->ai_protocol);
					listenAddr = a;
				}
			}			
			if (listenFd == SOCKET_ERROR) {
				freeaddrinfo(result);
				continue;
			}
			bListenRes = (0 == ::bind(listenFd, listenAddr->ai_addr, listenAddr->ai_addrlen) && 0 == ::listen(listenFd, 512));
			ct.closeSocket(listenFd);
			freeaddrinfo(result);
			if (!bListenRes)
			{
				continue;
			}
			// 成功
			break;
		}
		if (!bListenRes)
		{
			return false;
		}

		size_t iThreadCount = std::thread::hardware_concurrency();
		iThreadCount = 1 > (iThreadCount / 2) ? 2 : (iThreadCount / 2);
		for (auto i = 0; i < iThreadCount; ++i)
		{
			this->mRunThreadVec.push_back(std::thread(std::bind(&uHttpWebSocket::RunThreadImplement, this)));
		}
		return true;
	}

	bool uHttpWebSocket::UnInit()
	{
		//mInterrupted = true;
		// 设置退出，并发送退出消息
		for (size_t i = 0; i < mServerVec.size(); ++i)
		{
			mServerVec[i]->getDefaultGroup<uWS::SERVER>().setUserData((void*)1);
		}
		uWS::Hub h;
		std::map<std::string, std::string> extraHeaders;
		extraHeaders["closeServer"] = "true";
		char szAddr[64] = { 0 };
		snprintf(szAddr, _countof(szAddr), "ws://127.0.0.1:%d", miPort);
		for (size_t i = 0; i < mServerVec.size(); ++i)
		{
			h.connect(szAddr, NULL, extraHeaders, 1000);//, (void *)1, extraHeaders);
		}
		
		//for (size_t i = 0; i < mServerVec.size(); ++i)
		while(!mRunThreadVec.empty())
		{
			h.connect(szAddr, NULL, extraHeaders, 1000);//, (void *)1, extraHeaders);
			if (mRuningThreadCount.load(std::memory_order_relaxed) == 0)
			{
				break;
			}
			else
			{
				//std::this_thread::sleep_for(std::chrono::microseconds(10));
				std::this_thread::yield();
			}
			//qDebug("send close message");
		}
		//std::for_each(mServerVec.begin(), mServerVec.end(), [](uWS::Hub * h) { ((Group<SERVER>*)h)->close(); });
		h.getDefaultGroup<uWS::CLIENT>().close();
		h.getDefaultGroup<uWS::SERVER>().close();

		// 等待线程结束
		std::for_each(mRunThreadVec.begin(), mRunThreadVec.end(), [](std::thread &t) {
			t.join();
		});
		mRunThreadVec.clear();
		mServerVec.clear();

		return true;
	}

	/*void uHttpWebSocket::RunThread(uHttpWebSocket* ptrObj)
	{
		ptrObj->RunThreadImplement();
		return;
	}*/

	void uHttpWebSocket::RunThreadImplement()
	{
		++mRuningThreadCount;

		uWS::Hub h;
		static std::mutex mtx;
		mtx.lock();
		mServerVec.push_back(&h);
		mtx.unlock();

		h.onConnection([&h](uWS::WebSocket<uWS::SERVER> *ws, uWS::HttpRequest req) {
			//qDebug("onConnection<SERVER>()%d\n", std::this_thread::get_id());
		});

		h.onDisconnection([&h](uWS::WebSocket<uWS::SERVER> *ws, int code, char *message, size_t length) {
			//qDebug("onDisconnection<SERVER>()%d\n", std::this_thread::get_id());
		});

		h.onDisconnection([](uWS::WebSocket<uWS::CLIENT> *ws, int code, char *message, size_t length) {
			//qDebug("onDisconnection<CLIENT>()%d\n", std::this_thread::get_id());
			//qDebug("code:%d, length:%d, message:%s", code, length, message);
		});

		
		h.onHttpConnection([&](HttpSocket<SERVER> *hs) {
			//qDebug("onHttpConnection<SERVER>()%d\n", std::this_thread::get_id());
			if (h.getDefaultGroup<uWS::SERVER>().getUserData() == (void*)1)
			{// 关闭标志
				h.getDefaultGroup<uWS::SERVER>().close();
				//qDebug("receiveCloseMessage:%d\n", std::this_thread::get_id());
			}			

			/*std::map<std::string, std::string> extraHeaders;
			extraHeaders["closeServer"] = "true";
			h.connect("ws://127.0.0.1:3000", (void *)1, extraHeaders);
			h.getDefaultGroup<uWS::CLIENT>().close();*/

		});

		h.onHttpData([&](HttpResponse * res, char * data, size_t length, size_t remainingBytes) {
			//qDebug("onHttpData<>()%d\n", std::this_thread::get_id());
		});

		h.onCancelledHttpRequest([&](HttpResponse * res) {
			//qDebug("onCancelledHttpRequest<>()%d\n", std::this_thread::get_id());
		});

		//std::bind(&Foo::f1, &foo);

		//h.onMessage(&uHttpWebSocket::onMessage);
		h.getDefaultGroup<uWS::SERVER>().onMessage(std::bind(&uWS::uHttpWebSocket::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));

		//h.onHttpRequest(&uHttpWebSocket::onHttpRequest);
		h.getDefaultGroup<uWS::SERVER>().onHttpRequest(std::bind(&uHttpWebSocket::onHttpRequest, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));

		// This makes use of the SO_REUSEPORT of the Linux kernel
		// Other solutions include listening to one port per thread
		// with or without some kind of proxy inbetween
		if (!h.listen(miPort, nullptr, uS::ListenOptions::REUSE_PORT | uS::ListenOptions::ONLY_IPV4)) {
			//std::cout << "Failed to listen" << std::endl;
			return;
		}
		h.run();
		
		//qDebug("thread exist:%d\n", std::this_thread::get_id());

		--mRuningThreadCount;
	}

	bool uHttpWebSocket::SendToAllClient(std::string strData)
	{
		for (size_t i = 0; i < mServerVec.size(); ++i)
		{
			mServerVec[i]->getDefaultGroup<uWS::SERVER>().broadcast(strData.c_str(), strData.size(), uWS::TEXT);
		}
		return true;
	}

	void uHttpWebSocket::onMessage(uWS::WebSocket<uWS::SERVER> *ws, char *message, size_t length, uWS::OpCode opCode)
	{
		// 回显
		std::string strMessage;
		if (message && length > 0)
		{
			strMessage.assign(message, length);
			ws->send(message, length, opCode);
		}
		//qDebug("OpCode:%d, length:%d, message:%s", opCode, length, strMessage.c_str());
	}

	void uHttpWebSocket::onHttpRequest(HttpResponse *res, HttpRequest req, char *data, size_t length, size_t remainingBytes)
	{
		const std::string strHttpBaseDir = mstrWebRoot;// uHttpWebSocket::Instance().mstrWebRoot;

		std::string strUrl = req.getUrl().toString();
		//std::cout << req.getUrl().toString() << std::endl;

		if (!req.getMethod() == METHOD_GET)
		{
			res->end();
		}
		if (strUrl == "/")
		{
			strUrl += mstrDefaultTarget;// uHttpWebSocket::Instance().mstrDefaultTarget;
		}
		int iPosQuestionMark = strUrl.find_last_of('?');
		if (iPosQuestionMark != strUrl.npos)
		{// 去除参数
			strUrl = strUrl.substr(0, iPosQuestionMark);
		}
		strUrl = strHttpBaseDir + strUrl;


		std::string responseFile;
		// 读取文件
		FILE * fl = nullptr;
		fopen_s(&fl, strUrl.c_str(), "rb+");
		if (fl)
		{
			fseek(fl, 0, SEEK_END);
			long lFileLen = ftell(fl);
			responseFile.resize(lFileLen);
			fseek(fl, 0, SEEK_SET);
			long lReadLen = fread_s(&responseFile[0], responseFile.size(), 1, lFileLen, fl);
			fclose(fl);
			if (lReadLen < lFileLen)
			{
				//std::cout << "read fail" << std::endl;
			}
			if (strUrl.size() > 5 && strUrl.substr(strUrl.size() - 5) != ".html"
				&& responseFile.size() > 3 && responseFile[0] == -17 && responseFile[1] == -69 && responseFile[2] == -65)
			{// 去掉文件UTF-8格式头部，只对html文件进行处理
				responseFile = responseFile.substr(3);
			}
		}
		{// 判断文件类型并写入类型
			auto funcMimeType = [](const char *file)->char*
			{
				int n = strlen(file);
				if (n < 5)
					return NULL;

				if (!strcmp(&file[n - 4], ".ico"))
					return "image/x-icon";

				if (!strcmp(&file[n - 4], ".gif"))
					return "image/gif";

				if (!strcmp(&file[n - 3], ".js"))
					return "text/javascript";

				if (!strcmp(&file[n - 4], ".png"))
					return "image/png";

				if (!strcmp(&file[n - 4], ".jpg"))
					return "image/jpeg";

				if (!strcmp(&file[n - 3], ".gz"))
					return "application/gzip";

				if (!strcmp(&file[n - 4], ".JPG"))
					return "image/jpeg";

				if (!strcmp(&file[n - 5], ".html"))
					return "text/html";

				if (!strcmp(&file[n - 4], ".css"))
					return "text/css";

				if (!strcmp(&file[n - 4], ".txt"))
					return "text/plain";

				if (!strcmp(&file[n - 4], ".svg"))
					return "image/svg+xml";

				if (!strcmp(&file[n - 4], ".ttf"))
					return "application/x-font-ttf";

				if (!strcmp(&file[n - 4], ".otf"))
					return "application/font-woff";

				if (!strcmp(&file[n - 5], ".woff"))
					return "application/font-woff";

				if (!strcmp(&file[n - 4], ".xml"))
					return "application/xml";

				return NULL;
			};
			char * szMineType = funcMimeType(strUrl.c_str());
			if (szMineType)
			{//  如果这里没写入，则在返回文件数据时会自动添加一个头部"HTTP/1.1 200 OK\r\nContent-Length: %u\r\n\r\n"
				std::string strHeader = "HTTP/1.1 200 OK\r\n";
				strHeader += "content-type: " + std::string(szMineType) + "\r\n";
				strHeader += "connection: keep-alive\r\n";
				strHeader += "Content-Length: " + std::to_string(responseFile.size()) + "\r\n\r\n";
				res->write(strHeader.data(), strHeader.size());
			}
		}
		// 返回内容数据
		res->end(responseFile.data(), responseFile.size());
	}


	void uHttpWebSocket::GetLocalAddr(std::vector<std::string>& addrVec)
	{
		GetLocalAddrWindows(addrVec);
		//GetLocalAddrQt(addrVec);
		return;
	}

	void uHttpWebSocket::GetLocalAddrQt(std::vector<std::string>& addrVec)
	{
		QList<QNetworkInterface> interfaceList = QNetworkInterface::allInterfaces();
		foreach(QNetworkInterface interfaceItem, interfaceList)
		{
			//qDebug("#######################################################");
			// 过滤部分无效网卡
			if (!interfaceItem.isValid()
				|| !interfaceItem.flags().testFlag(QNetworkInterface::IsUp)
				|| !interfaceItem.flags().testFlag(QNetworkInterface::IsRunning)
				|| !interfaceItem.flags().testFlag(QNetworkInterface::CanBroadcast)
				|| !interfaceItem.flags().testFlag(QNetworkInterface::CanMulticast)
				|| interfaceItem.flags().testFlag(QNetworkInterface::IsLoopBack)
				)
			{
				continue;
			}
			// 过滤虚拟网卡 VMware Network Adapter VMnet1  ||  vEthernet
			if (interfaceItem.name().contains("VMware Network Adapter")
				|| interfaceItem.humanReadableName().contains("VMware Network Adapter")
				|| interfaceItem.name().contains("vEthernet")
				|| interfaceItem.humanReadableName().contains("vEthernet")
				)
			{
				continue;
			}

			QList<QNetworkAddressEntry> addressEntryList = interfaceItem.addressEntries();
			foreach(QNetworkAddressEntry addressEntryItem, addressEntryList)
			{
				if (addressEntryItem.ip().protocol() == QAbstractSocket::IPv4Protocol)
				{

					//qDebug() << "------------------------------------------------------------";
					//qDebug() << "Adapter Name:" << interfaceItem.name();
					//qDebug() << "Adapter Address:" << interfaceItem.hardwareAddress();
					//qDebug() << "type:" << interfaceItem.type();
					//qDebug() << "humanReadableName:" << interfaceItem.humanReadableName();
					//qDebug() << "dnsEligibility:" << addressEntryItem.dnsEligibility(); 
					//qDebug() << "IP Address:" << addressEntryItem.ip().toString();
					//qDebug() << "IP Mask:" << addressEntryItem.netmask().toString();
					//qDebug() << "broadcast:" << addressEntryItem.broadcast().toString();

					/*qDebug() << addressEntryItem.ip().toString()
					<< " protocol:" << addressEntryItem.ip().protocol()
					<< " isLoopback:" << addressEntryItem.ip().isLoopback()
					<< " isGlobal:" << addressEntryItem.ip().isGlobal()
					<< " isLinkLocal:" << addressEntryItem.ip().isLinkLocal()
					<< " isSiteLocal:" << addressEntryItem.ip().isSiteLocal()
					<< " isUniqueLocalUnicast:" << addressEntryItem.ip().isUniqueLocalUnicast()
					<< " isMulticast:" << addressEntryItem.ip().isMulticast()
					<< " isBroadcast:" << addressEntryItem.ip().isBroadcast()
					<< " scopeId:" << addressEntryItem.ip().scopeId();*/

					addrVec.push_back(addressEntryItem.ip().toString().toStdString());
				}
			}
		}
	}


#include <TlHelp32.h>
	//#include <winsock2.h>
#include <iphlpapi.h>
#include <SHELLAPI.H>
	// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")
	//#pragma comment(lib, "ws2_32.lib")
	void uHttpWebSocket::GetLocalAddrWindows(std::vector<std::string>& addrVec)
	{
		ULONG Family = AF_UNSPEC;
		ULONG Flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS;
		PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL;
		ULONG SizePointer = 0;
		ULONG ulRes = GetAdaptersAddresses(Family, Flags, NULL, AdapterAddresses, &SizePointer);
		if (ERROR_BUFFER_OVERFLOW == ulRes)
		{
			AdapterAddresses = (PIP_ADAPTER_ADDRESSES)malloc(static_cast<size_t>(SizePointer));
			if (ERROR_SUCCESS == GetAdaptersAddresses(Family, Flags, NULL, AdapterAddresses, &SizePointer))
			{
				for (PIP_ADAPTER_ADDRESSES pCurrAddresses = AdapterAddresses; pCurrAddresses; pCurrAddresses = pCurrAddresses->Next)
				{
					//qDebug() << "##############################################################";
					if (pCurrAddresses->OperStatus != IfOperStatusUp)
					{// 跳过不可用的网卡
						continue;
					}
					if (pCurrAddresses->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
					{
						//qDebug() << "本地循环网络";
						continue;
					}
					if (pCurrAddresses->PhysicalAddressLength != 6)
					{// 127.0.0.1没有mac
						continue;
					}
					if (!pCurrAddresses->FirstGatewayAddress)
					{// 没有默认网关的地址，估计外网不能访问，发现虚拟机的虚拟网卡都没有默认网关
						continue;
					}
					// 过滤虚拟网卡 
					// VMware Network Adapter VMnet1(VMware Virtual Ethernet Adapter for VMnet1)
					// vEthernet(Hyper-V Virtual Ethernet Adapter)
					if (std::wstring(pCurrAddresses->FriendlyName).find(L"VMware Network Adapter") != std::wstring::npos
						|| std::wstring(pCurrAddresses->FriendlyName).find(L"vEthernet") != std::wstring::npos
						)
					{
						continue;
					}

					//LogPrintfEx("AdapterName:%s", pCurrAddresses->AdapterName);
					//qDebug() << "DnsSuffix:" << QString::fromWCharArray(pCurrAddresses->DnsSuffix);
					//qDebug() << "Description:" << QString::fromWCharArray(pCurrAddresses->Description);
					//qDebug() << "FriendlyName:" << QString::fromWCharArray(pCurrAddresses->FriendlyName);

					//qDebug() << "Flags:" << pCurrAddresses->Flags;
					if (0 == (pCurrAddresses->Flags & IP_ADAPTER_IPV4_ENABLED))
					{
						//qDebug() << "不包含IPV4";
					}

					//qDebug() << "IfType:" << pCurrAddresses->IfType;
					if (pCurrAddresses->IfType == IF_TYPE_PROP_VIRTUAL)
					{
						//qDebug() << "虚拟网络";
					}

					//qDebug() << "OperStatus:" << pCurrAddresses->OperStatus;

					//qDebug() << "ConnectionType" << pCurrAddresses->ConnectionType;

					{// MAC
						bool isUppercase = true;
						bool horizontalLine = false;
						std::wstringstream strMacStream;
						strMacStream.flags(std::ios::right | std::ios::hex);//| std::ios::showbase );
						if (isUppercase)
						{
							strMacStream.setf(std::ios::uppercase);
						}
						else
						{
							strMacStream.unsetf(std::ios::uppercase);
						}
						strMacStream.width(2);
						strMacStream.fill('0');
						for (unsigned int i = 0; i < pCurrAddresses->PhysicalAddressLength; ++i)
						{
							strMacStream << (int)pCurrAddresses->PhysicalAddress[i];
							if (horizontalLine && i != pCurrAddresses->PhysicalAddressLength - 1)
							{
								strMacStream << L"-";
							}
						}
						std::wstring strMac = strMacStream.str();
						//qDebug() << "MAC:" << QString::fromStdWString(strMac);
					}

					// 显示测试函数
					auto funcOutputAdress = [](SOCKET_ADDRESS& Address, char * pstrPrefix) {
						CHAR IP[128] = { 0 };
						if (AF_INET == Address.lpSockaddr->sa_family)// IPV4 地址，使用 IPV4 转换
						{
							inet_ntop(PF_INET, &((sockaddr_in*)Address.lpSockaddr)->sin_addr, IP, sizeof(IP));
							//LogPrintfEx("%s:%s", pstrPrefix, IP);
						}
						//else if (AF_INET6 == Address.lpSockaddr->sa_family)// IPV6 地址，使用 IPV6 转换
						//{
						//	inet_ntop(PF_INET6, &((sockaddr_in6*)Address.lpSockaddr)->sin6_addr, IP, sizeof(IP));
						//	LogPrintfEx("%s:%s", pstrPrefix, IP);
						//}
					};
					//单播IP
					for (auto pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast; pUnicast = pUnicast->Next)
					{
						//funcOutputAdress(pUnicast->Address, "Unicast Address");
						CHAR IP[128] = { 0 };
						if (AF_INET == pUnicast->Address.lpSockaddr->sa_family)// IPV4 地址，使用 IPV4 转换
						{
							inet_ntop(PF_INET, &((sockaddr_in*)pUnicast->Address.lpSockaddr)->sin_addr, IP, sizeof(IP));
							//LogPrintfEx("%s:%s", pstrPrefix, IP);
							addrVec.push_back(IP);
						}
					}
					/*
					// 任播地址
					for (auto pAnycastAddress = pCurrAddresses->FirstAnycastAddress; pAnycastAddress; pAnycastAddress = pAnycastAddress->Next)
					{
					funcOutputAdress(pAnycastAddress->Address, "Anycast Address");
					}
					// 广播地址
					for (auto pMulticastAddress = pCurrAddresses->FirstMulticastAddress; pMulticastAddress; pMulticastAddress = pMulticastAddress->Next)
					{
					funcOutputAdress(pMulticastAddress->Address, "Multicast Address");
					}
					// DNS
					for (auto pDnsServerAddress = pCurrAddresses->FirstDnsServerAddress; pDnsServerAddress; pDnsServerAddress = pDnsServerAddress->Next)
					{
					funcOutputAdress(pDnsServerAddress->Address, "Dns Server Address");
					}
					// Wins Server Address
					for (auto pWinsServerAddress = pCurrAddresses->FirstWinsServerAddress; pWinsServerAddress; pWinsServerAddress = pWinsServerAddress->Next)
					{
					funcOutputAdress(pWinsServerAddress->Address, "Wins Server Address");
					}
					// 网关
					for (auto pGatewayAddress = pCurrAddresses->FirstGatewayAddress; pGatewayAddress; pGatewayAddress = pGatewayAddress->Next)
					{
					funcOutputAdress(pGatewayAddress->Address, "Gateway Address");
					}
					// 适配器前缀
					for (auto pPrefix = pCurrAddresses->FirstPrefix; pPrefix; pPrefix = pPrefix->Next)
					{
					funcOutputAdress(pPrefix->Address, "Adapter Prefix");
					}
					//DHCP服务器地址
					if (pCurrAddresses->Dhcpv4Server.lpSockaddr)
					{
					funcOutputAdress(pCurrAddresses->Dhcpv4Server, "Dhcp v4 Server");
					}
					*/

				}
			}
			if (AdapterAddresses)
			{
				free(AdapterAddresses);
			}
		}
		return;
	}



}